package com.snowfield.lb47.lbconfig.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.snowfield.lb47.lbconfig.domain.*;
import com.snowfield.lb47.lbconfig.service.*;
import com.snowfield.lbcommon.domain.Cluster;
import com.snowfield.lbcommon.service.IClusterService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 前端代理Controller
 *
 * @author snowfield
 * @date 2024-04-01
 */
@RestController
@RequestMapping("/lbconfig/frontproxy")
public class FrontProxyController extends BaseController {
    private static final Logger LOGGER = LoggerFactory.getLogger(FrontProxyController.class);
    private static final String ACCESS_KEY = "snowfield-9151e473-4eda-4258-a455-632df4c2af9a";

    @Autowired
    private IFrontProxyService frontProxyService;
    @Autowired
    private IBackserverService backserverService;
    @Autowired
    private IFrontProxyCrtService frontProxyCrtService;
    @Autowired
    private IBackserverCrtService backserverCrtService;
    @Autowired
    private IBlackwhiteListService blackwhiteListService;
    @Autowired
    private IHttpCheckParamService httpCheckParamService;
    @Autowired
    private IClusterService clusterService;

    /**
     * 查询前端代理列表
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:list')")
    @GetMapping("/list")
    public TableDataInfo list(FrontProxy frontProxy) {
        startPage();
        List<FrontProxy> list = frontProxyService.selectFrontProxyList(frontProxy);
        return getDataTable(list);
    }

    /**
     * 导出前端代理列表
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:export')")
    @Log(title = "前端代理", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, FrontProxy frontProxy) {
        List<FrontProxy> list = frontProxyService.selectFrontProxyList(frontProxy);
        ExcelUtil<FrontProxy> util = new ExcelUtil<FrontProxy>(FrontProxy.class);
        util.exportExcel(response, list, "前端代理数据");
    }

    /**
     * 获取前端代理详细信息
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(frontProxyService.selectFrontProxyById(id));
    }

    /**
     * 新增前端代理
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:add')")
    @Log(title = "前端代理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody FrontProxy frontProxy) throws JsonProcessingException {
        if (!frontProxyService.checkNameUnique(frontProxy)) {
            return AjaxResult.error("新增前端代理 '" + frontProxy.getProxyName() + "'失败，名称已存在");
        }
        if (!frontProxyService.checkBindIpPortUnique(frontProxy)) {
            return AjaxResult.error("新增前端代理 '" + frontProxy.getProxyName() + "'失败，绑定地址和端口已存在");
        }
        frontProxy.setCreateBy(getUsername());

        if ("httpservice".equals(frontProxy.getHealthCheck()) && frontProxy.getCheckParamId() != 0){
            return AjaxResult.error("后台【自定义检测参数】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getStickTable())){
            return AjaxResult.error("后台【粘贴表】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getCompression())){
            return AjaxResult.error("后台【数据压缩】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getCache())){
            return AjaxResult.error("后台【数据缓存】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getShowAttack())){
            return AjaxResult.error("后台【慢攻击防护】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getDdos())){
            return AjaxResult.error("后台【Ddos防护】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getAntibot())){
            return AjaxResult.error("后台【机器人攻击防护】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        return toAjax(frontProxyService.insertFrontProxy(frontProxy));
    }

    /**
     * 修改前端代理
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:edit')")
    @Log(title = "前端代理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody FrontProxy frontProxy) {
        if (!frontProxyService.checkNameUnique(frontProxy)) {
            return AjaxResult.error("修改前端代理 '" + frontProxy.getProxyName() + "'失败，名称已存在");
        }
        if (!frontProxyService.checkBindIpPortUnique(frontProxy)) {
            return AjaxResult.error("修改前端代理 '" + frontProxy.getProxyName() + "'失败，绑定地址和端口已存在");
        }
        frontProxy.setUpdateBy(getUsername());

        if ("httpservice".equals(frontProxy.getHealthCheck()) && frontProxy.getCheckParamId() != 0){
            return AjaxResult.error("后台【自定义检测参数】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getStickTable())){
            return AjaxResult.error("后台【粘贴表】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getCompression())){
            return AjaxResult.error("后台【数据压缩】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getCache())){
            return AjaxResult.error("后台【数据缓存】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getShowAttack())){
            return AjaxResult.error("后台【慢攻击防护】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getDdos())){
            return AjaxResult.error("后台【Ddos防护】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        if ("1".equals(frontProxy.getAntibot())){
            return AjaxResult.error("后台【机器人攻击防护】功能未生效！如有需要请联系作者QQ:3961919621");
        }

        return toAjax(frontProxyService.updateFrontProxy(frontProxy));
    }

    /**
     * 删除前端代理
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:remove')")
    @Log(title = "前端代理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        return toAjax(frontProxyService.deleteFrontProxyByIds(ids));
    }

    /**
     * 修改前端代理状态
     * 无论开启或关闭，都需要更新ha配置文件并reload
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:edit')")
    @Log(title = "前端代理", businessType = BusinessType.UPDATE)
    @PostMapping("/changeProxyStatus/{id}/{status}")
    public AjaxResult changeProxyStatus(@PathVariable Long id, @PathVariable String status) {
        //1.更新代理状态
        Integer result = frontProxyService.changeProxyStatus(id, status);

        AjaxResult ajaxResult;
        HashMap<String, String> data = new HashMap<>();
//        result = 1;
        if (result == 0) {
            data.put("resultCode", "0");
            data.put("msg", "状态修改失败");
            ajaxResult = AjaxResult.success(data);
        } else if (result == 1) {
            data.put("resultCode", "1");
            data.put("msg", "状态修改成功");
            ajaxResult = AjaxResult.success(data);
        } else {
            ajaxResult = AjaxResult.success();
        }
        return ajaxResult;
    }

    /**
     * reload策略
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:edit')")
    @Log(title = "前端代理", businessType = BusinessType.UPDATE)
    @GetMapping("/reloadProxy")
    public AjaxResult reloadProxy() {
        //1.reload策略
        Boolean result = frontProxyService.reloadProxy();

        AjaxResult ajaxResult;
        HashMap<String, String> data = new HashMap<>();
        if (result) {
            data.put("resultCode", "1");
            data.put("msg", "重载策略成功");
            ajaxResult = AjaxResult.success(data);
        } else {
            data.put("resultCode", "0");
            data.put("msg", "重载策略失败");
            ajaxResult = AjaxResult.success(data);
        }
        return ajaxResult;
    }

    /**
     * 返回服务器信息，以Map形式返回，内容包括服务器信息及证书信息
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:list')")
    @GetMapping("/serverMap")
    public AjaxResult getServerMap() {
        HashMap<Long, Backserver> serverMap = new HashMap<>();
        Backserver queryForm = new Backserver();
        List<Backserver> backserverList = backserverService.selectBackserverList(queryForm);
        for (Backserver item : backserverList) {
            serverMap.put(item.getId(), item);
        }

        return AjaxResult.success(serverMap);
    }

    /**
     * 返回代理证书信息，以Map形式返回，内容包括服务器信息及证书信息
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:list')")
    @GetMapping("/proxyCrtMap")
    public AjaxResult getProxyCrtMap() {
        HashMap<Long, String> proxyCrtMap = new HashMap<>();
        FrontProxyCrt queryForm = new FrontProxyCrt();
        List<FrontProxyCrt> proxyCrtList = frontProxyCrtService.selectFrontProxyCrtList(queryForm);
        for (FrontProxyCrt proxyCrt : proxyCrtList) {
            proxyCrtMap.put(proxyCrt.getId(), proxyCrt.getCrtName());
        }

        return AjaxResult.success(proxyCrtMap);
    }

    /**
     * 返回后端服务器证书信息，以Map形式返回，内容包括服务器信息及证书信息
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:list')")
    @GetMapping("/serverCrtMap")
    public AjaxResult getServerCrtMap() {
        HashMap<String, String> backServerCrtMap = new HashMap<>();
        List<BackserverCrt> backserverCrtList = backserverCrtService.selectBackserverCrtList(new BackserverCrt());
        for (BackserverCrt backserverCrt : backserverCrtList) {
            if ("1".equals(backserverCrt.getStatus())) {
                backServerCrtMap.put(backserverCrt.getServerId() + ":" + backserverCrt.getCrtPort(), backserverCrt.getCrtName());
            }
        }

        return AjaxResult.success(backServerCrtMap);
    }

    /**
     * 返回黑白名单信息，以Map形式返回
     * 按类型分成黑名单信息及白名单信息返回
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:list')")
    @GetMapping("/blackwhiteListMap")
    public AjaxResult getBlackwhiteListMap() {
        HashMap<String, HashMap<Long, String>> blackwhiteListMap = new HashMap<>();

        // <listId,listName>
        HashMap<Long, String> blackListMap = new HashMap<>();
        // <listId,listName>
        HashMap<Long, String> whiteListMap = new HashMap<>();

        BlackwhiteList queryForm = new BlackwhiteList();
        List<BlackwhiteList> blackwhiteListList = blackwhiteListService.selectBlackwhiteListList(queryForm);
        for (BlackwhiteList item : blackwhiteListList) {
            if (item.getListType().equals("black")) {
                blackListMap.put(item.getId(), item.getListName());
            } else if (item.getListType().equals("white")) {
                whiteListMap.put(item.getId(), item.getListName());
            }
        }
        blackwhiteListMap.put("blackList", blackListMap);
        blackwhiteListMap.put("whiteList", whiteListMap);

        return AjaxResult.success(blackwhiteListMap);
    }

    /**
     * 返回检测参数信息，以Map形式返回，内容包括标识ID,名称
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:list')")
    @GetMapping("/checkParamMap")
    public AjaxResult getCheckParamMap() {
        HashMap<Long, String> checkParamMap = new HashMap<>();
        HttpCheckParam queryForm = new HttpCheckParam();
        List<HttpCheckParam> checkParamList = httpCheckParamService.selectHttpCheckParamList(queryForm);
        for (HttpCheckParam item : checkParamList) {
            checkParamMap.put(item.getId(), item.getParamName());
        }

        return AjaxResult.success(checkParamMap);
    }

    /**
     * 返回集群配置信息
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:list')")
    @GetMapping("/cluster")
    public AjaxResult getClusterInfo() {
        Cluster cluster = clusterService.selectClusterById(1L);
        return AjaxResult.success(cluster);
    }

    /**
     * 检测代理配置是否正常
     */
    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:edit')")
    @Log(title = "前端代理", businessType = BusinessType.UPDATE)
    @GetMapping("/checkProxyConfig/{id}")
    public AjaxResult checkProxyConfig(@PathVariable Long id) {
        //1.更新代理状态
        String checkResult = frontProxyService.checkProxyConfig(id);

        HashMap<String, String> resultData = new HashMap<>();
        resultData.put("configCheckResult", checkResult);
        AjaxResult ajaxResult = AjaxResult.success(resultData);
        return ajaxResult;
    }

    /**
     * 对外接口
     * 对外提供本机运行策略绑定的IP地址
     * 在集群环境下，如果两个节点未及时同步的话，虚拟路由绑定的VIP是不同的，导致KA报异常，
     * info: receive an invalid ip number count associated with VRID!
     * info: bogus VRRP packet received on eth0 !!!
     * info: VRRP_Instance(VI_1) Dropping received VRRP packet...
     * 正常情况下，不影响业务；但是在未知原因情况下，会导致两个节点都绑定了VIP，从而引起多节点IP地址冲突，并影响业务
     * 所以对多个节点间VIP地址进行比较，将未同步的VIP地址单独配置于static_ipaddress中，其中的VIP不会进行飘移
     */
//    @PreAuthorize("@ss.hasPermi('lbconfig:frontproxy:edit')")
//    @Log(title = "前端代理", businessType = BusinessType.UPDATE)
    @RateLimiter(count = 1, time = 10) //限流每10秒1次
    @GetMapping("/getVips")
    public AjaxResult getAllVip(
            HttpServletRequest request,
            @RequestParam("access_key") String access_key) {

        if (StringUtils.isEmpty(access_key) || !ACCESS_KEY.equals(access_key)) {
            return AjaxResult.error("access_key is wrong");
        }
        //查询集群配置信息
        Cluster cluster = clusterService.selectClusterById(1L);
        //对端设备IP地址
        String peerIp = cluster.getPeerIp();

        String remoteIp = IpUtils.getIpAddr(request);
        LOGGER.info("remoteIp : " + remoteIp);
//        peerIp = remoteIp;
        //发起同步的设备地址与集群配置中允许的 对端设备IP地址不同
        if (!"127.0.0.1".equals(remoteIp) && !"0:0:0:0:0:0:0:1".equals(remoteIp) && !StringUtils.equals(remoteIp, peerIp)) {
            return AjaxResult.error("非法访问地址");
        }

        //1.查询所有的前端代理
        FrontProxy queryForm = new FrontProxy();
        queryForm.setStatus("1");
        List<FrontProxy> frontProxyList = frontProxyService.selectFrontProxyList(queryForm);

        List<String> allVipList = new ArrayList<>();
        for (FrontProxy frontProxy : frontProxyList){
            allVipList.add(frontProxy.getBindIp());
        }
        AjaxResult ajaxResult = AjaxResult.success(allVipList);
        return ajaxResult;
    }

}
